package com.zhitan.netty.service;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.*;
import com.zhitan.netty.ChannelRepository;
import com.zhitan.netty.bean.ChargingOrder;
import com.zhitan.netty.bean.FeeRangeTime;
import com.zhitan.netty.bean.OTAFtpServer;
import com.zhitan.netty.enums.*;
import com.zhitan.netty.util.CRC16Modbus;
import com.zhitan.netty.util.HexHandleUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

import static com.zhitan.netty.constant.DataMapKeyConstant.*;
import static com.zhitan.netty.constant.NettyConstant.*;

/**
 * Channel基础类
 */
@Slf4j
public class BaseChannelHandlerService {
    /**
     * 序列号域即为数据包的发送顺序号，从 0 开始顺序增加，如是应答数据包，则与询问数据包
     * 序号保持一致，当桩与平台网络断开重新建立连接或者溢出后归 0。
     */
    protected static short PACKAGE_INDEX = 0;
    /**
     * 默认物理卡号
     */
    protected static final String DEFAULT_PHYSICAL_CARD_NO = "00000000";
    /**
     * FTP HOST最大位数
     */
    protected static final int FTP_MAX_SIZE_16 = 16;
    /**
     * FTP FILE PATH 最大位数
     */
    protected static final int FTP_MAX_SIZE_32 = 32;
    /**
     * 物理卡号列表最大位数
     */
    protected static final int PHYSICAL_CARD_NO_LIST_MAX_SIZE = 24;
    /**
     * 默认订单号
     */
    protected static final String DEFAULT_ORDER_NO = "00000000000000000000000000000000";
    protected static final String BYTE_TO_STRING_REX = "0x%02X";

    /**
     * 读取SIM卡号 0x76
     *
     * @param deviceNo 设备ID
     */
    public void readSimCardNo(String deviceNo) {

        if (null == deviceNo || "".equals(deviceNo)) {
            log.error("读取SIM卡号失败!设备号为空!");
            return;
        }

        ByteBuf byteBuf = Unpooled.buffer();
        FrameTypeEnum cmd = FrameTypeEnum.READ_SIM_CARD_INFO;
        writeByteBufStart(byteBuf, cmd.getResLen(), getPackageIndex(), cmd.getCode());
        // 设备编号 BCD 7
        byteBuf.writeBytes(ByteBufUtil.decodeHexDump(deviceNo));
        writeByteCRCAndResetReadIndex(byteBuf, cmd.getResLen());

        Map<String, Object> msgBody = new ConcurrentHashMap<>();
        msgBody.put(KEY_DEVICE_NO, deviceNo);

        saveMessage(
                deviceNo,
                ByteBufUtil.hexDump(byteBuf),
                DEFAULT_ORDER_NO,
                msgBody.toString(),
                cmd,
                PileStatusEnum.UNKNOWN);

        ChannelRepository.sendMsgToDevice(getLogKey(), deviceNo, byteBuf);
    }

    /**
     * 远程重启 0x92
     * 重启充电桩，应对部分问题，如卡死等
     *
     * @param deviceNo 设备号
     * @param type     0立即执行 1空闲执行
     */
    public void reboot(String deviceNo, int type) {

        if (null == deviceNo || "".equals(deviceNo)) {
            log.error("远程重启失败!设备号为空!");
            return;
        }

        ByteBuf byteBuf = Unpooled.buffer();
        FrameTypeEnum cmd = FrameTypeEnum.REBOOT_REQ;
        writeByteBufStart(byteBuf, cmd.getResLen(), getPackageIndex(), cmd.getCode());
        // 设备编号
        byteBuf.writeBytes(HexUtil.decodeHex(deviceNo));
        // 是否工作
        byteBuf.writeByte(type);
        writeByteCRCAndResetReadIndex(byteBuf, cmd.getResLen());

        Map<String, Object> msgBody = new ConcurrentHashMap<>();
        msgBody.put(KEY_DEVICE_NO, deviceNo);
        msgBody.put(KEY_SET_RESULT, type);

        saveMessage(
                deviceNo,
                ByteBufUtil.hexDump(byteBuf),
                DEFAULT_ORDER_NO,
                msgBody.toString(),
                cmd,
                PileStatusEnum.UNKNOWN);

        ChannelRepository.sendMsgToDevice(getLogKey(), deviceNo, byteBuf);
    }

    /**
     * 远程更新 0x94
     * 对桩进行软件升级，平台升级模式为ftp文件升级，由桩企提供升级需要的更新文件（文件名由桩企定义）
     * 平台再数据帧中提供访问更新文件相关服务器地址及下载路径信息，桩下载完更新程序后对文件进行校验并进行升级。
     *
     * @param deviceNo 设备号
     * @param maxPower 最大功率
     * @param server   服务器
     */
    protected void senOTAUpdateCMD(String deviceNo, int maxPower, OTAFtpServer server, int execUploadType) {

        ByteBuf byteBuf = Unpooled.buffer();
        FrameTypeEnum cmd = FrameTypeEnum.OTA_REQ;
        writeByteBufStart(byteBuf, cmd.getResLen(), getPackageIndex(), cmd.getCode());
        // 桩编码  7位BCD码
        byteBuf.writeBytes(HexUtil.decodeHex(deviceNo));
        // 桩型号 1位BIN码 小端模式
        byteBuf.writeByte(PileModelEnum.AC.getCode());
        // 桩功率 2位BIN码 小端模式
        byteBuf.writeShortLE(maxPower * 1000);
        // 升级服务器地址  16位ASCII码 不足补0
        byte[] hostAscBytes = server.getHost().getBytes(StandardCharsets.US_ASCII);
        fillZero(hostAscBytes, byteBuf, FTP_MAX_SIZE_16);

        // 升级服务器端口  2位BIN码
        byteBuf.writeShortLE(Integer.parseInt(server.getPort()));

        byte[] usernameAscBytes = server.getUserName().getBytes(StandardCharsets.US_ASCII);
        fillZero(usernameAscBytes, byteBuf, FTP_MAX_SIZE_16);

        // 密码  16位ASCII码 不足补0
        byte[] passwordAscBytes = server.getPassword().getBytes(StandardCharsets.US_ASCII);
        fillZero(passwordAscBytes, byteBuf, FTP_MAX_SIZE_16);

        // 文件路径 32位ASCII码 不足补0
        byte[] filePathAscBytes = server.getPath().getBytes(StandardCharsets.US_ASCII);
        fillZero(filePathAscBytes, byteBuf, FTP_MAX_SIZE_32);

        // 执行控制 1位BIN码 01:立即执行 02:空闲执行
        byteBuf.writeByte(execUploadType == 0 ? ControlTypeEnum.NOW.getCode() : ControlTypeEnum.FREE.getCode());
        // 下载超时时间 1位BIN码 单位：分钟
        byteBuf.writeByte(server.getTimeout());
        // 校验
        writeByteCRCAndResetReadIndex(byteBuf, cmd.getResLen());

        Map<String, Object> msgBody = new ConcurrentHashMap<>();
        msgBody.put(KEY_DEVICE_NO, deviceNo);
        msgBody.put(KEY_FTP_HOST, server.getHost());
        msgBody.put(KEY_FTP_PORT, server.getPort());
        msgBody.put(KEY_FTP_USER, server.getUserName());
        msgBody.put(KEY_FTP_PWD, server.getPassword());
        msgBody.put(KEY_FTP_PATH, server.getPath());
        msgBody.put(KEY_FTP_TIME_OUT, server.getTimeout());
        msgBody.put(KEY_FTP_CONTROLLER_TYPE, ControlTypeEnum.NOW.getCode());

        saveMessage(
                deviceNo,
                ByteBufUtil.hexDump(byteBuf),
                DEFAULT_ORDER_NO,
                msgBody.toString(),
                cmd,
                PileStatusEnum.UNKNOWN);

        ChannelRepository.sendMsgToDevice(getLogKey(), deviceNo, byteBuf);
    }

    /**
     * 位数不够，进行补零操作
     *
     * @param originByte 原始byte
     * @param byteBuf    byteBuf
     * @param maxSize    最大值
     */
    private void fillZero(byte[] originByte, ByteBuf byteBuf, int maxSize) {
        byteBuf.writeBytes(originByte);
        if (maxSize - originByte.length > 0) {
            byteBuf.writeZero(maxSize - originByte.length);
        }
    }

    protected void setFeeModelInfo(String logKey, ByteBuf byteBuf, String deviceNo) {
        //TODO 结合实际自己实现
//        List<CustomPrice> priceList = customPriceMapper.queryDevicePrice(deviceNo);
//        if (ObjectUtils.isNotEmpty(priceList)) {
//            priceList.sort(Comparator.comparing(CustomPrice::getPriceType));
//            // 时间段信息
//            List<FeeRangeTime> rangeTimeList = new ArrayList<>();
//            for (CustomPrice customPrice : priceList) {
//                Integer price = Convert.toInt(new BigDecimal(customPrice.getPrice()).multiply(BigDecimal.valueOf(100000)));
//                Integer servicePrice = Convert.toInt(customPrice.getServicePrice().multiply(BigDecimal.valueOf(100000)));
//                // 设置电费费率 默认 0
//                byteBuf.writeZero(4);
//                // 设置服务费费率
//                byteBuf.writeIntLE(servicePrice);
//                // 处理时间信息
//                String rangeTime = customPrice.getRangTime();
//                List<PriceResponse.TimeRange> rangeTimes = JSONUtil.toList(rangeTime, PriceResponse.TimeRange.class);
//                for (PriceResponse.TimeRange time : rangeTimes) {
//                    FeeRangeTime feeRangeTime = new FeeRangeTime();
//                    BeanUtil.copyProperties(time, feeRangeTime);
//                    feeRangeTime.setPriceType(customPrice.getPriceType());
//                    rangeTimeList.add(feeRangeTime);
//                }
//            }
        // 计损比例设置
        byteBuf.writeByte(0x00);
        // 按startTime排序rangeTimeList
        List<FeeRangeTime> rangeTimeList = new ArrayList<>();
        rangeTimeList.sort(Comparator.comparing(FeeRangeTime::getStartTime));
        // 计算48个时间段每个时间段的价格编号
        for (FeeRangeTime feeRangeTime : rangeTimeList) {
            Integer num = Convert.toInt((feeRangeTime.getEndTime() - feeRangeTime.getStartTime()) / 0.5);
            if (num > 0) {
                for (int i = 0; i < num; i++) {
                    byteBuf.writeByte(feeRangeTime.getPriceType());
                }
            }
        }
    }

    /**
     * 桩启动处理
     *
     * @param orderId     订单编号
     * @param startResult 启动结果
     * @param errorCode   错误代码
     */
    protected void orderPileStartRes(String orderId, byte startResult, StartChargingErrorEnum errorCode) {
//        UserOrderDto order = orderMapper.queryUserOrderByOrderNumber(orderId);
//        OrderLog orderLog = new OrderLog();
//        // 如果启动成功，更新心跳数据 0x00失败 0x01成功
//        if (startResult == 1) {
//            // 1.0 更新命令情况
//            deviceStateMapper.updateCommandOK(orderId);
//            // 2.0 更新订单充电状态为开始充电
//            orderMapper.updateOrderStart(orderId);
//            // 启动日志key
//            String startInfo = RedisKeyConstant.MESSAGE_START + orderId;
//            // 不存在，才需要发送消息
//            if (!redisUtils.checkExist(startInfo)) {
//                // 3.0 插入日志
//                orderLog = OrderLog
//                        .builder()
//                        .mainProcess(MAIN_PROCESS_TRUE)
//                        .orderNumber(orderId)
//                        .briefInfo("开始充电")
//                        .logContent("远程启动充电成功，开始充电")
//                        .createTime(new Date())
//                        .build();
//                // 放入信息，避免200s内重复发送的情况
//                redisUtils.set(startInfo, orderId, 200);
//            }
//        } else {
//            // 1.0 更新命令情况
//            deviceStateMapper.updateCommandErrorCode(orderId, errorCode.getCode());
//            // 2.0 更新订单状态，取消订单
//            orderMapper.cancelOrder(orderId);
//            // 3.0 释放端口
//            portMapper.releasePortById(order.getParkLockId());
//            // 4.0 插入日志
//            orderLog = OrderLog
//                    .builder()
//                    .mainProcess(MAIN_PROCESS_TRUE)
//                    .orderNumber(orderId)
//                    .briefInfo("充电启动异常")
//                    .logContent("充电桩未正常开启，异常原因" + errorCode.getDesc())
//                    .createTime(new Date())
//                    .build();
//        }
//        orderLogMapper.insert(orderLog);
    }

    /**
     * 创建离线订单的订单号
     *
     * @param orderNo 订单号
     */
    protected String createOfflineOrderNo(String orderNo) {
        //TODO 根据自己实际去情况实现
        return "";
    }

    /**
     * 结束订单
     *
     * @param dataMap map数据
     */

    protected ChargingOrder handleOrderOverState(Map<String, Object> dataMap) {
        ChargingOrder orderInfo = new ChargingOrder();
        //TODO 根据自己实际去情况实现 需要什么自己拿可以了
        // 订单号
        String orderNumber = (String) dataMap.get(KEY_TRANSACTION_NO);
        // 总充电量
        double consumePower = (Double) dataMap.get(KEY_TOTAL_POWER);
        // 停止原因
        ChargingStopReasonEnum stopReason = ChargingStopReasonEnum.getByCode((byte) dataMap.get(KEY_STOP_REASON));
        log.debug("停止原因:{}", stopReason.getReason());
        // 订单开始和结束时间
        String startTime = (String) dataMap.get(KEY_START_TIME);
        String endTime = (String) dataMap.get(KEY_END_TIME);
        // 总金额
        double totalAmount = (double) dataMap.get(KEY_TOTAL_AMOUNT);
        return orderInfo;
    }

    /**
     * 保存订单结算数据
     */
    private void saveOrderFlow(Map<String, Object> dataMap) {
        //TODO 根据自己实际去情况实现 需要什么自己拿可以了
        // 订单号
        dataMap.get(KEY_TRANSACTION_NO).toString();
        // 充电桩编号
        ByteBufUtil.hexDump((byte[]) dataMap.get(KEY_DEVICE_NO));
        // 枪号
        dataMap.get(KEY_GUN_NO).toString();
        // 开始时间
        dataMap.get(KEY_START_TIME).toString();
        // 结束时间
        dataMap.get(KEY_END_TIME).toString();
        // 交易时间
        dataMap.get(KEY_TRANSACTION_TIME).toString();
        // TODO 以下所有的都是0x3B中的数据

        double jf = (double) dataMap.get(KEY_PRICE_0);
        double jp = (double) dataMap.get(KEY_POWER_0);
        double jlp = (Integer) dataMap.get(KEY_LOSE_0);
        double ja = (Integer) dataMap.get(KEY_AMOUNT_0);

        double ff = (double) dataMap.get(KEY_PRICE_1);
        double fp = (double) dataMap.get(KEY_POWER_1);
        double flp = (Integer) dataMap.get(KEY_LOSE_1);
        double fa = (Integer) dataMap.get(KEY_AMOUNT_1);

        double pf = (double) dataMap.get(KEY_PRICE_2);
        double pp = (double) dataMap.get(KEY_POWER_2);
        double plp = (Integer) dataMap.get(KEY_LOSE_2);
        double pa = (Integer) dataMap.get(KEY_AMOUNT_2);

        double gf = (double) dataMap.get(KEY_PRICE_3);
        double gp = (double) dataMap.get(KEY_POWER_3);
        double glp = (Integer) dataMap.get(KEY_LOSE_3);
        double ga = (Integer) dataMap.get(KEY_AMOUNT_3);
        // 总电量
        double tp = (Double) dataMap.get(KEY_TOTAL_POWER);
        // 总电量
        int tlp = (Integer) dataMap.get(KEY_LOSE_POWER);
        // 订单总金额
        double ta = (Double) dataMap.get(KEY_TOTAL_AMOUNT);
        // 交易标志
        dataMap.get(KEY_TRANSACTION_FLAG);
        // 停止原因
        dataMap.get(KEY_STOP_REASON);
        // 卡号
        dataMap.get(KEY_CARD_NO);
    }

    /**
     * 更新设备停止状态
     * 0X35 远程停机命令
     */
    protected void updateDeviceStopState(String deviceNo, Map<String, Object> dataMap) {
        //TODO 根据自己实际去情况实现
        // 停止结果 0x00失败 0x01成功
        byte stopResult = (byte) dataMap.get(KEY_STOP_RESULT);
        RemoteStopErrorCodeEnum errorReason = RemoteStopErrorCodeEnum.getByCode((byte) dataMap.get(KEY_ERROR_REASON));
        // 枪编号
        byte gunNo = (byte) dataMap.get(KEY_GUN_NO);
        // TODO 成功失败自己逻辑
    }

    /**
     * 充电过程数据存储
     *
     * @param deviceNo     设备编号
     * @param originalData 原始报文
     * @param dataMap      数据
     * @param orderType    订单类型 0-普通订单 1-离线订单
     */
    protected ChargingOrder saveOrderProcess(String deviceNo, String originalData, Map<String, Object> dataMap, Integer orderType) {
        // 1.0 解析数据
        String transactionNo = (String) dataMap.get(KEY_TRANSACTION_NO);
        BigDecimal totalFee = BigDecimal.valueOf((Double) dataMap.get(KEY_TOTAL_FEE));
        double outVoltage = (double) dataMap.get(KEY_OUT_VOLTAGE);
        double outCurrent = (double) dataMap.get(KEY_OUT_CURRENT);
        // 累计充电时间
        short outTime = (short) dataMap.get(KEY_OUT_TIME);
        // 已充度数
        BigDecimal hasChargePower = BigDecimal.valueOf((Double) dataMap.get(KEY_HAS_CHARGE_POWER));
        short surTime = (short) dataMap.get(KEY_SUR_TIME); // 估算剩余充电时间
        Integer soc = ByteUtil.byteToUnsignedInt((byte) dataMap.get(KEY_SOC));
        PileStatusEnum gunStatus = PileStatusEnum.getByCode((byte) dataMap.get(KEY_GUN_STATUS));
        // TODO 存库
        return null;
    }

    /**
     * 计算服务费
     *
     * @param orderNumber    订单编号
     * @param deviceNo       设备号
     * @param hasChargePower 已充电量度数
     * @return 计算服务费
     */
    protected BigDecimal calculateServiceFee(String orderNumber, String deviceNo, BigDecimal hasChargePower) {
        //TODO 根据自己实际去情况实现
        return null;
    }

    /**
     * 实时监测数据处理
     *
     * @param dataMap  数据
     * @param deviceNo 设备编号
     */
    protected void sendWebSocketMessage(Map<String, Object> dataMap, String deviceNo) {

        //TODO 根据自己实际去情况实现 给websocket发送实时状态
        dataMap.get(KEY_TRANSACTION_NO);
        dataMap.get(KEY_DEVICE_NO);
        dataMap.get(KEY_GUN_NO);
        // 状态 0x00：离线 0x01：故障 0x02：空闲 0x03：充电 0x04:充电桩就绪  需做到变位上送
        dataMap.get(KEY_GUN_STATUS);
        // 枪是否归位  0x00 否 0x01 是 0x02 未知
        dataMap.get(KEY_GUN_HOMING);
        // 是否插枪 0x00 否 0x01 是
        dataMap.get(KEY_GUN_INSERT);
        // 输出电压 精确到小数点后一位；待机置零
        dataMap.get(KEY_OUT_VOLTAGE);
        // 输出电流
        dataMap.get(KEY_OUT_CURRENT);
        // 枪线温度  一个字节
        dataMap.get(KEY_TEMPERATURE);

        // BMS 当前荷电状态 SOC（ %） 1%/位，0%偏移量；数据范围：0~100%
        dataMap.get(KEY_SOC);
        dataMap.get(KEY_MAX_TEMPERATURE);
        // 累计充电时间 1 min/位，0 min 偏移量；数据范围：0~600 min
        dataMap.get(KEY_OUT_TIME);
        // 剩余时间 单位：min；待机置零、交流桩置零
        dataMap.get(KEY_SUR_TIME);
        // 充电度数 精确到小数点后四位；待机置零
        dataMap.get(KEY_HAS_CHARGE_POWER);
        // 已充金额
        dataMap.get(KEY_TOTAL_FEE);
        dataMap.get(KEY_CODE_ERROR);
    }

    protected void updatePortStatus(Map<String, Object> dataMap, String deviceNo) {
        //TODO 根据自己实际去情况实现
        String transactionNo = (String) dataMap.get(KEY_TRANSACTION_NO);
        String gunNo = String.valueOf(dataMap.get(KEY_GUN_NO)); //端口
        String gunStatus = String.valueOf(dataMap.get(KEY_GUN_STATUS)); //端口状态 0:离线 1:故障 2:空闲 3:充电
        String gunInsert = String.valueOf(dataMap.get(KEY_GUN_INSERT)); // 是否插枪 0 否 1 是
        String gunHoming = String.valueOf(dataMap.get(KEY_GUN_HOMING)); //枪是否归位  0 否 1 是 2 未知
        String codeError = String.valueOf(dataMap.get(KEY_CODE_ERROR)); //异常编码
    }

    /**
     * 保存错误日志
     *
     * @param deviceNo 设备号
     * @param code     code
     */
    protected void saveErrorLog(String deviceNo, String code) {
        try {
            String hardwareError = getDeviceError(Short.parseShort(code));
            //TODO 保存错误日志
        } catch (Exception ex) {
            log.error("硬件故障保存失败");
        }
    }

    /**
     * 充电桩状态：0离线 1空闲-未插枪 2空闲-已插枪 3充电中 4已充满 5故障 6就绪
     * TODO 自定义内容，自己转换到自己业务
     *
     * @param pileStatusEnum 充电桩状态 -1:未知 0x00：离线 0x01：故障 0x02：空闲 0x03：充电
     * @param gunInsert      插枪状态 0x00 否 0x01 是 0x02 未知
     * @return
     */
    protected int getPileStatus(PileStatusEnum pileStatusEnum, String gunInsert) {
        int pileStatus;
        switch (pileStatusEnum) {
            // 故障
            case ERR:
                pileStatus = WebSocketPileStatusEnum.FAULT.getCode();
                break;
            // 空闲
            case FREE:
                pileStatus = WebSocketPileStatusEnum.IDLE_NO_GUNS.getCode();
                if (gunInsert.equals("1")) {
                    pileStatus = WebSocketPileStatusEnum.IDLE_GUNS.getCode();
                }
                break;
            case CHARGING: // 充电
                pileStatus = WebSocketPileStatusEnum.CHARGING.getCode();
                break;
            case READY:
                pileStatus = WebSocketPileStatusEnum.READY.getCode();
                break;
            case UNKNOWN:
            case OFFLINE:
            default:
                pileStatus = WebSocketPileStatusEnum.OFFLINE.getCode();
                break;
        }
        return pileStatus;
    }

    /**
     * 获取log key
     */
    protected String getLogKey() {
        return UUID.fastUUID().toString();
    }

    /**
     * 计算充电电费
     *
     * @param deviceNo 设备id
     * @param dataMap  数据
     * @return 充电电费数据
     */
    protected double computePowerFee(String deviceNo, Map<String, Object> dataMap) {
        double power0 = (double) dataMap.get(KEY_POWER_0);
        double power1 = (double) dataMap.get(KEY_POWER_1);
        double power2 = (double) dataMap.get(KEY_POWER_2);
        double power3 = (double) dataMap.get(KEY_POWER_3);
        //TODO: 通过桩号查找计费规则，然后计算
//        List<BigDecimal> priceList = queryDevicePrice(deviceNo);
        double chargeFee = 0;
//        for (BigDecimal customPrice : priceList) {
//            byte priceType = customPrice.getPriceType();
//            // 只收取服务费
//            double servicePrice = Convert.toDouble(customPrice.getServicePrice());
//            if (priceType == 0) {
//                chargeFee += NumberUtil.mul(servicePrice, power0);
//            } else if (priceType == 1) {
//                chargeFee += NumberUtil.mul(servicePrice, power1);
//            } else if (priceType == 2) {
//                chargeFee += NumberUtil.mul(servicePrice, power2);
//            } else if (priceType == 3) {
//                chargeFee += NumberUtil.mul(servicePrice, power3);
//            }
//        }
        return NumberUtil.round(chargeFee, 4).doubleValue();
    }

    /**
     * 组装报文的开始信息
     *
     * @param byteBuf      报文
     * @param dataLength   报文长度
     * @param packageIndex 包序号
     * @param frameType    帧类型
     */
    protected void writeByteBufStart(ByteBuf byteBuf, byte dataLength, short packageIndex, byte frameType) {
        // 起始数据编写
        byteBuf.writeByte(FrameTypeEnum.START.getCode());
        // 数据长度
        byteBuf.writeByte(dataLength);
        // 数据序号
        byteBuf.writeShortLE(packageIndex);
        // 加密标识
        byteBuf.writeByte(0x00);
        // 帧类型
        byteBuf.writeByte(frameType);
    }

    /**
     * 编写帧校验位并重置指针位置
     *
     * @param byteBuf    响应数据
     * @param dataLength 数据长度
     */
    protected void writeByteCRCAndResetReadIndex(ByteBuf byteBuf, int dataLength) {
        // 校验位
        byteBuf.writeShortLE(CRC16Modbus.get(HexHandleUtil.byteBufToBytes(byteBuf, dataLength)));
        // 重置 ByteBuf 读指针位置
        byteBuf.resetReaderIndex();
    }

    /**
     * 获取报序号
     *
     * @return 包序号
     */
    protected short getPackageIndex() {
        if (PACKAGE_INDEX >= MAX_PACKAGE_INDEX) {
            PACKAGE_INDEX = 0;
        } else {
            PACKAGE_INDEX++;
        }
        return PACKAGE_INDEX;
    }

    /**
     * 保存消息日志
     *
     * @param deviceNo    设备号
     * @param originalStr 原始报文
     * @param orderNo     订单号
     * @param messageBody 报文体
     * @param cmd         帧类型
     * @param pileStatus  桩状态
     */
    protected void saveMessage(String deviceNo, String originalStr,
                               String orderNo, String messageBody,
                               FrameTypeEnum cmd, PileStatusEnum pileStatus) {
        handlerAsync(() ->
        {
            //TODO: 保存协议交互的消息，方便查找问题
            return null;
        });
    }

    /**
     * 处理数据
     */
    protected <U> void handlerAsync(Supplier<U> supplier) {
        CompletableFuture.supplyAsync(supplier);
    }

    /**
     * 获取设备错误
     *
     * @param errorCode 错误代码
     */
    private String getDeviceError(short errorCode) {
        // 将数据转换为二进制 按位读取
        String binaryString = Integer.toBinaryString(errorCode);
        //整体从右往左读取
        binaryString = org.apache.commons.lang3.StringUtils.reverse(binaryString);
        List<String> hardwareError = new ArrayList<>();
        for (int i = 0; i < binaryString.length(); i++) {
            char c = binaryString.charAt(i);
            if (c == '1') {
                hardwareError.add(DeviceErrorEnum.getByCode(i).getMessage());
            }
        }
        return String.join(",", hardwareError);
    }
}
